﻿using System;
using System.Messaging;
using log4net;

namespace SLS.ExClassLib.MultiThreading.Threading.ThreadPools.TaskItems
{
    public abstract class TransactionalMsmqTaskItem<T> : ITaskItem
        where T : class
    {
        public MessageQueue Queue { get; set; }

        #region ITaskItem Members

        public void DoWork()
        {
            try
            {
                T message = null;
                TimeSpan messageReceiveTimeout = TimeSpan.FromMilliseconds(500);
                using (var transaction = new MessageQueueTransaction())
                {
                    try
                    {
                        transaction.Begin();
                        Message msmqMessage = Queue.Receive(messageReceiveTimeout, transaction);

                        if (msmqMessage != null)
                        {
                            message = msmqMessage.Body as T;

                            if (message == null)
                            {
                                string messageTypeStr = msmqMessage.Body != null
                                                            ? msmqMessage.Body.GetType().Name
                                                            : "<null>";
                                LogManager.GetLogger(GetType()).Warn(string.Format(
                                                                         "Message of a wrong type was received (expected {0} but was {1}",
                                                                         typeof (T).Name, messageTypeStr));
                            }
                            else
                            {
                                Perform(message);
                            }
                        }

                        transaction.Commit();
                    }
                    catch (Exception exp)
                    {
                        transaction.Abort();
                        LogManager.GetLogger(GetType()).Error(exp);
                        AbortDoWork(transaction, message);
                        return;
                    }
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(GetType()).Error(ex);
            }
        }

        #endregion

        protected virtual void AbortDoWork(MessageQueueTransaction transaction, T message)
        {
            transaction.Abort();
        }

        protected abstract void Perform(T value);
    }
}